package com.heima.article.job;

import com.alibaba.fastjson.JSON;
import com.heima.article.service.HotArticleService;
import com.heima.model.common.constants.article.HotArticleConstants;
import com.heima.model.mess.app.ArticleVisitStreamMess;
import com.heima.model.mess.app.UpdateArticleMess;
import com.rabbitmq.client.Return;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.BinaryOperator;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Component
@Slf4j
public class UpdateHotArticleJob {
    @Autowired
    StringRedisTemplate redisTemplate;
@Autowired
    HotArticleService hotArticleService;


    @XxlJob("updateHotArticleJob")
    public ReturnT updateHotArticleHandler(String params){
        log.info("热文章分值更新 调度任务开始执行....");
        // TODO 定时更新文章热度
        //1.在redis行为列表中获取数据
        List<UpdateArticleMess>articleMessesList =getUpdateArticleMesses();
        if (CollectionUtils.isEmpty(articleMessesList)){
            log.info("热文章分值更新：未产生任何文章行为 调度任务完成。。。 ");
            return ReturnT.SUCCESS;
        }
        //2.将数据按文章分组  进行聚合统计  得到待更新的数据列表
        List<ArticleVisitStreamMess>waitUpdateScoreData=getArticleVisitStreamMesses(articleMessesList);
        if (CollectionUtils.isEmpty(waitUpdateScoreData)){
            log.info("热文章分值更新: 太冷清了 未产生任何文章行为 调度任务完成....");
            return ReturnT.SUCCESS;
        }
        // 3. TODO 更新数据库文章分值
        waitUpdateScoreData.forEach(hotArticleService::updateApArticle);//调用hotArticleService 修改 每个文章热度值
        log.info("热文章分值更新: 调度任务完成....");
        return ReturnT.SUCCESS;
    }
    /**
     * 按文章分组  每个文章的所有行为 进行聚合处理
     * @param
     * @return
     */
    private List<ArticleVisitStreamMess> getArticleVisitStreamMesses(List<UpdateArticleMess> articleMessesList) {
        ArrayList<ArticleVisitStreamMess>  waitUpdateScoreData  = new ArrayList<>();
        //1。按照文章的id分组，获取对应的分组下的文章列表
       Map<Long,List<UpdateArticleMess>>map=articleMessesList.stream()
               .collect(Collectors.groupingBy(UpdateArticleMess::getArticleId));
        //2。计算每个分组的结果
        map.forEach((articleId,messList)->{
            Optional<ArticleVisitStreamMess> reduceResult=messList.stream()
                    .map(articleMes-> {
                        ArticleVisitStreamMess visitStreamMess = new ArticleVisitStreamMess();
                        visitStreamMess.setArticleId(articleId);
                        switch(articleMes.getType()){
                            case LIKES:
                            // 设置 点赞数量
                            visitStreamMess.setLike(articleMes.getAdd());
                            break;
                            case VIEWS:
                                // 设置 阅读数量
                                visitStreamMess.setView(articleMes.getAdd());
                                break;
                            case COMMENT:
                                // 设置 评论数量
                                visitStreamMess.setComment(articleMes.getAdd());
                                break;
                            case COLLECTION:
                                // 设置 收藏数量
                                visitStreamMess.setCollect(articleMes.getAdd());
                                break;
                        }
                        return visitStreamMess;
                    }).reduce(new BinaryOperator<ArticleVisitStreamMess>() {
                        /**
                         * 归并运算
                         * @param a1
                         * @param a2
                         * @return
                         */
                        @Override
                        public ArticleVisitStreamMess apply(ArticleVisitStreamMess a1, ArticleVisitStreamMess a2) {
                           a1.setLike(a1.getLike()+a2.getLike());
                           a1.setArticleId(a1.getArticleId()+a2.getArticleId());
                           a1.setCollect(a1.getCollect()+a2.getCollect());
                           a1.setComment(a1.getComment()+a2.getComment());
                            return a1;
                        }
                    });
            waitUpdateScoreData.add(reduceResult.get());
            if (reduceResult.isPresent()){
                //聚合结果
                ArticleVisitStreamMess visitStreamMess = reduceResult.get();
                log.info("热点文章 聚合计算结果==》{}",visitStreamMess);
                waitUpdateScoreData.add(visitStreamMess);
            }
        });
        return waitUpdateScoreData;

    }

    /**
     * 获取redis list列表中的待处理行为数据
     * @return
     */

    private List<UpdateArticleMess> getUpdateArticleMesses() {
        //1.获取redis的行为列表中待处理数据
        ListOperations listOperations =redisTemplate.opsForList();
        //得到当前的行为
        Long size = listOperations.size(HotArticleConstants.HOT_ARTICLE_SCORE_BEHAVIOR_LIST);
        //采用管道命令，让多个命令保证原子性
        List result=redisTemplate.executePipelined(new RedisCallback<List<UpdateArticleMess>>() {
            @Override
            public List<UpdateArticleMess>doInRedis(RedisConnection connection)throws DataAccessException {
                //开启管道执行命令
                connection.openPipeline();
                //获取0到size-1的所有集合的数据
                connection.lRange(HotArticleConstants.HOT_ARTICLE_SCORE_BEHAVIOR_LIST.getBytes(),0,(size-1));
                //截断size到-1后续的集合数据
                connection.lTrim(HotArticleConstants.HOT_ARTICLE_SCORE_BEHAVIOR_LIST.getBytes(),size,-1);
                return null;

            }
        }, RedisSerializer.string());
        if (result.size()>0){
            List<String>listData=( List<String>)result.get(0);
            return listData.stream()
                    .map(str -> JSON.parseObject(str, UpdateArticleMess.class))
                    .collect(Collectors.toList());
        }
        return null;
        }

    }


